package net.sansi.v3correctserver.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import net.sansi.v3correctserver.exception.ServiceException;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.UUID;

/**
 * Created by Fang
 * create time : 2024/11/1
 * IDE : IntelliJ IDEA
 */
@Slf4j
public class CmmUtil {

    /**
     * 线程休眠
     *
     * @param ms 毫秒
     */
    public static void sleep(long ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            log.error("线程休眠异常", e);
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 从控制台输入内容
     *
     * @param tip 提示信息
     * @return 输入内容
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        System.out.println(tip);
        if (scanner.hasNext()) {
            return scanner.next();
        }
        return "";
    }

    /**
     * 从控制台读取输入
     *
     * @param command 读取
     * @return 输入内容
     */
    public static String readFromSystemIn(String command) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println(command);
        String input = null;
        try {
            input = reader.readLine();
        } catch (IOException e) {
            throw new ServiceException("读取输入内容失败！", e);
        }
        return input;
    }

    /**
     * 获取项目所在的绝对路径
     */
    public static Path appPath() {
        return Path.of(System.getProperty("user.dir"));
    }

    /**
     * 从 JoinPoint 中获取指定注解
     *
     * @param joinPoint 切入点
     * @param target    注解类
     * @return 注解
     */
    public static <T extends Annotation> T getAnnotation(JoinPoint joinPoint, Class<T> target) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        return method.getAnnotation(target);
    }


    /**
     * 解析 SpEl 表达式
     * 需要传入一个上下文
     * 一般用于多个对象解析的情况
     * <p>
     * 例如：
     * StandardEvaluationContext context = new StandardEvaluationContext();
     * context.setVariable("user", new User("John"));
     * context.setVariable("profile", new Profile("1234567890"));
     *
     * @param spEl    表达式 如：1 + 2 或 'username=' + #user.name
     * @param context 上下文
     * @return 解析结果
     */
    public static Object parseSpEl(String spEl, StandardEvaluationContext context) {
        try {
            if (context == null || StrUtil.isBlank(spEl)) return parseSpEl(spEl);
            ExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(spEl);
            return expression.getValue(context);
        } catch (Exception e) {
            log.error("解析 SpEl 出错：{}", spEl, e);
            return spEl;
        }
    }

    /**
     * 解析 SpEl 表达式
     *
     * @param spEl 表达式 如：1 + 2 或 'username=' + #user.name
     * @return 解析结果
     */
    public static Object parseSpEl(String spEl) {
        try {
            if (StrUtil.isBlank(spEl)) return spEl;
            ExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(spEl);
            return expression.getValue();
        } catch (Exception e) {
            log.error("解析 SpEl 出错：{}", spEl, e);
            return spEl;
        }
    }

    /**
     * 解析 SpEl 表达式
     * 直接从切入点中获取参数
     *
     * @param spEl      表达式 如：1 + 2 或 'username=' + #user.name
     * @param joinPoint 切入点
     * @return 解析结果
     */
    public static Object parseSpEl(String spEl, JoinPoint joinPoint) {
        return parseSpEl(spEl, getArgsMap(joinPoint));
    }


    /**
     * 解析 SpEl 表达式
     * 可直接传入一个对象
     *
     * @param spEl   表达式 如：1 + 2 或 'username=' + #user.name
     * @param params 参数
     * @return 解析结果
     */
    public static Object parseSpEl(String spEl, Map<String, Object> params) {
        if (params == null) return parseSpEl(spEl);
        StandardEvaluationContext context = new StandardEvaluationContext();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            context.setVariable(entry.getKey(), entry.getValue());
        }
        return parseSpEl(spEl, context);
    }

    /**
     * 解析 SpEl 表达式
     * 仅仅对某一个参数进行解析
     *
     * @param spEl  表达式 如：1 + 2 或 'username=' + #user.name
     * @param key   参数名
     * @param value 参数
     * @return 解析结果
     */
    public static Object parseSpEl(String spEl, String key, Object value) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setVariable(key, value);
        return parseSpEl(spEl, context);
    }

    /**
     * 获取切入点的参数名称和参数值
     *
     * @param joinPoint 切入点
     * @return 参数名称和参数值
     */
    public static Map<String, Object> getArgsMap(JoinPoint joinPoint) {
        Map<String, Object> ans = new HashMap<>();
        String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < argNames.length; i++) {
            ans.put(argNames[i], args[i]);
        }
        return ans;
    }


    /**
     * 缓存设置
     *
     * @param cacheName 缓存名称
     * @param key       键
     * @param value     值
     */
    public static void cacheSet(String cacheName, Object key, Object value) {
        CacheManager manager = SpringUtil.getBean(CacheManager.class);
        Cache cache = manager.getCache(cacheName);
        if (cache != null) {
            cache.put(key, value);
        } else {
            log.error("缓存设置失败，缓存不存在：{}", cacheName);
        }
    }

    /**
     * 缓存获取
     *
     * @param cacheName 缓存名称
     * @param key       键
     * @return 值
     */
    public static <T extends Object> T cacheGet(String cacheName, Object key, T def) {
        Object res = cacheGet(cacheName, key);
        if (res == null) {
            return def;
        }
        return (T) res;
    }

    /**
     * 缓存获取
     *
     * @param cacheName 缓存名称
     * @param key       键
     * @return 值
     */
    public static Object cacheGet(String cacheName, Object key) {
        CacheManager manager = SpringUtil.getBean(CacheManager.class);
        Cache cache = manager.getCache(cacheName);
        if (cache != null) {
            Cache.ValueWrapper wrapper = cache.get(key);
            if (wrapper != null) return wrapper.get();
        }
        return null;
    }

    /**
     * 缓存回调函数
     * 当找不到缓存的时候，会调用回调函数
     */
    public interface CacheCallback {
        Object cache();
    }

    /**
     * 缓存获取
     *
     * @param cacheName 缓存名称
     * @param key       key
     * @param callback  回调函数
     * @param <T>       类型
     * @return 值
     */
    public static <T extends Object> T cacheGetCallback(String cacheName, Object key, CacheCallback callback) {
        Object res = cacheGet(cacheName, key);
        if (res == null) {
            res = callback.cache();
            if (res == null) return null;
            cacheSet(cacheName, key, res);
        }
        return (T) res;
    }

    /**
     * 缓存获取，如果不存在则添加
     *
     * @param cacheName 缓存名称
     * @param key       键
     * @param def       默认值
     * @param <T>       类型
     */
    public static <T extends Object> T cacheGetAdd(String cacheName, Object key, T def) {
        Object res = cacheGet(cacheName, key);
        if (res == null) {
            res = def;
            cacheSet(cacheName, key, res);
        }
        return (T) res;
    }

    public interface CacheGetAddCallback<T> {
        T get();
    }

    /**
     * 缓存获取，如果不存在则添加
     *
     * @param cacheName 缓存名称
     * @param key       键
     * @param callback  默认值
     * @param <T>       类型
     */
    public static <T extends Object> T cacheGetAdd(String cacheName, Object key, CacheGetAddCallback<T> callback) {
        Object res = cacheGet(cacheName, key);
        if (res == null) {
            res = callback.get();
            if (res == null) return null;
            cacheSet(cacheName, key, res);
        }
        return (T) res;
    }

    /**
     * 缓存删除
     *
     * @param cacheName 缓存名称
     * @param key       键
     */
    public static void cacheEvict(String cacheName, Object key) {
        CacheManager manager = SpringUtil.getBean(CacheManager.class);
        Cache cache = manager.getCache(cacheName);
        if (cache != null) {
            cache.evict(key);
        }
    }

    /**
     * 缓存清空
     *
     * @param cacheName 缓存名称
     */
    public static void cacheClear(String cacheName) {
        CacheManager manager = SpringUtil.getBean(CacheManager.class);
        Cache cache = manager.getCache(cacheName);
        if (cache != null) {
            cache.clear();
        }
    }

    /**
     * 获取当前请求的 HttpServletRequest
     *
     * @return HttpServletRequest
     */
    public static HttpServletRequest getCurrentHttpRequest() {
        ServletRequestAttributes requestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            return requestAttributes.getRequest();
        }
        // 如果在非Web请求环境中，可能返回null
        return null;
    }

    /**
     * 写入请求参数
     *
     * @param key   键
     * @param value 值
     */
    public static void setRequestAttribute(String key, Object value) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) return;
        requestAttributes.setAttribute(key, value, RequestAttributes.SCOPE_REQUEST);
    }

    /**
     * 获取请求参数
     *
     * @param key 键
     * @return 值
     */
    public static Object getRequestAttribute(String key) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) return null;
        return requestAttributes.getAttribute(key, RequestAttributes.SCOPE_REQUEST);
    }


    /**
     * 获取当前时间戳
     */
    public static Long getTimeStamp() {
        return System.currentTimeMillis() / 1000;
    }

    /**
     * 获取远程IP地址
     *
     * @return IP地址
     */
    public static String getRemoteIP() {
        try {
            HttpServletRequest request = getCurrentHttpRequest();
            if (request == null) return "unknown";
            String ip = request.getHeader("x-forwarded-for");
            if (ipIsNull(ip)) ip = request.getHeader("Proxy-Client-IP");
            if (ipIsNull(ip)) ip = request.getHeader("X-Forwarded-For");
            if (ipIsNull(ip)) ip = request.getHeader("WL-Proxy-Client-IP");
            if (ipIsNull(ip)) ip = request.getHeader("X-Real-IP");
            if (ipIsNull(ip)) ip = request.getRemoteAddr();
            if (ipIsNull(ip)) return "unknown";
            String firstIp = ip.split(",")[0].trim();
            if (firstIp.length() > 15) {
                log.error("获取到错误IP地址：{}", ip);
                return firstIp.substring(0, 15);
            }
            return "0:0:0:0:0:0:0:1".equals(firstIp) ? "127.0.0.1" : firstIp;
        } catch (Exception e) {
            log.error("获取远程IP地址失败", e);
            return "unknown";
        }
    }

    /**
     * 判断读取到的远程IP是否为空
     *
     * @param ip ip
     * @return 是否为空
     */
    private static boolean ipIsNull(String ip) {
        return ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip);
    }

    /**
     * 创建临时文件
     * 快速创建一个以 UUID 命名的临时文件
     */
    public static File createTempFile() throws IOException {
        File file = File.createTempFile(UUID.randomUUID().toString(), ".tmp");
        file.deleteOnExit();
        return file;
    }

    /**
     * 删除文件
     * 删除文件，如果删除失败则记录错误信息
     *
     * @param file   文件
     * @param errMsg 错误信息
     */
    public static void deleteFile(File file, String errMsg) {
        if (file == null || !file.exists()) return;
        if (!file.delete()) {
            log.error("删除文件失败：{}，{}", file.getAbsolutePath(), errMsg);
        }
    }

    /**
     * 读取文件
     * 并且自动剔除 BOM 头
     * 这里BOM头中必须是UTF8编码
     *
     * @param file 文件
     * @throws IOException 读取异常
     */
    public static String readFileWithoutBOM(File file) throws IOException {
        StringBuilder sb = new StringBuilder();
        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] bomBytes = new byte[3];
            int numRead = fis.read(bomBytes);
            if (numRead == 3 && bomBytes[0] == (byte) 0xEF && bomBytes[1] == (byte) 0xBB && bomBytes[2] == (byte)
                    0xBF) {
                log.info("{}, {}", file.getAbsoluteFile(), "发现UTF8 BOM头，跳过头读取。");
            } else {
                fis.getChannel().position(Math.max(0, fis.getChannel().position() - numRead));
            }
            int row = 0;
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(fis, StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    sb.append(line).append("\n");
                    if (row++ == 0) log.info("读取到文件第一行：{}", line.getBytes(StandardCharsets.UTF_8));
                }
            }
            if (sb.length() > 0) sb.deleteCharAt(sb.length() - 1);
            return sb.toString();
        }
    }

    /**
     * 文件转换为 MultipartFile
     *
     * @param file 文件
     * @return MultipartFile
     */
    public static MultipartFile convertToMultipartFile(File file) {
        try {
            // 创建一个 DiskFileItem 实例
            FileItem fileItem = new DiskFileItem(
                    "file", // 表单字段名
                    "application/octet-stream", // 文件类型
                    false, // 是否为表单字段
                    file.getName(), // 文件名
                    (int) file.length(), // 文件大小
                    file.getParentFile() // 临时文件目录
            );
            // 将文件内容写入 FileItem
            java.io.FileInputStream input = new java.io.FileInputStream(file);
            java.io.OutputStream os = fileItem.getOutputStream();
            byte[] buffer = new byte[1024 * 1024];
            int bytesRead;
            while ((bytesRead = input.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.flush();
            os.close();
            input.close();
            // 使用 CommonsMultipartFile 包装 FileItem
            return new CommonsMultipartFile(fileItem);
        } catch (Exception e) {
            throw new ServiceException("文件转换为 MultipartFile 失败 {}", file.getAbsoluteFile(), e);
        }
    }

    /**
     * 判断当前环境是否为生产环境
     *
     * @return true or false
     */
    public static boolean isProdEnv() {
        return "prod".equals(SpringUtil.getActiveProfile());
    }

    /**
     * 判断当前环境是否为开发环境
     *
     * @return true or false
     */
    public static boolean isDevEnv() {
        return "dev".equals(SpringUtil.getActiveProfile());
    }

    /**
     * 判断当前环境是否为测试环境
     *
     * @return true or false
     */
    public static boolean isTestEnv() {
        return "test".equals(SpringUtil.getActiveProfile());
    }

    /**
     * 判断当前环境是否为本地环境
     *
     * @return true or false
     */
    public static boolean isLocalEnv() {
        return "test".equals(SpringUtil.getActiveProfile());
    }

}
